#include "VulkanApplication.h"
#include <vector>
#include <iostream>

void VulkanApplication::cleanupSwapchain()
{
    for (size_t i = 0; i < _framebuffers.size(); ++i)
        vkDestroyFramebuffer(_logicDevice, _framebuffers[i], nullptr);
    for (size_t i = 0; i < _swapchainImageViews.size(); ++i)
        vkDestroyImageView(_logicDevice, _swapchainImageViews[i], nullptr);
    if (_swapChain != nullptr) vkDestroySwapchainKHR(_logicDevice, _swapChain, nullptr);
}

uint32_t VulkanApplication::recreateSwapchain(int& width, int& height, VkSemaphore waitImage)
{
    VkResult result = vkDeviceWaitIdle(_logicDevice);
    if (result != VK_SUCCESS)
    {
        printf("Failed to wait for graphics queue.\n");
        return 0;
    }

    glfwGetWindowSize(_window, &width, &height);
    cleanupSwapchain();

    bool recreated = createSwapchain();
    if (recreated) recreated = createImageViews();
    if (recreated) recreated = createFrameBuffers(width, height);

    uint32_t imageIndex = 0;
    if (recreated)
    {
        result = vkAcquireNextImageKHR(_logicDevice, _swapChain, UINT64_MAX,
                                       waitImage, VK_NULL_HANDLE, &imageIndex);
        if (result != VK_SUCCESS)
        {
            printf("Failed to get next image from swapchain.\n");
            return 0;
        }
    }
    return imageIndex;
}

int main(int argc, char** argv)
{
    VulkanApplication app("c08_recreating", 1024, 768);
    if (!app.initialized())
    {
        printf("Failed to initialize VulkanApplication.\n");
        return 1;
    }

    // Main loop
    int width = 1024, height = 768;
    VkFence waitFence = app.getOrCreateFence("CommandFence");
    VkSemaphore waitNextImage = app.getOrCreateSemaphore("WaitNextImage");
    VkSemaphore waitSubmission = app.getOrCreateSemaphore("WaitSubmission");
    VkCommandBuffer commandBuffer = app.getCommandBuffer();
    while (!app.shouldClose())
    {
        vkWaitForFences(app.getDevice(), 1, &waitFence, VK_TRUE, UINT64_MAX);
        vkResetFences(app.getDevice(), 1, &waitFence);

        uint32_t imageIndex = 0;
        VkResult result = vkAcquireNextImageKHR(app.getDevice(), app.getSwapchain(), UINT64_MAX,
                                                waitNextImage, VK_NULL_HANDLE, &imageIndex);
        switch (result)
        {
        case VK_SUBOPTIMAL_KHR:
        case VK_ERROR_OUT_OF_DATE_KHR:
            imageIndex = app.recreateSwapchain(width, height, waitNextImage); break;
        case VK_SUCCESS:
            printf("Next image: %d\n", imageIndex); break;
        default:
            printf("Next image: %d (RESULT = %d)\n", imageIndex, result);
            app.setClosed(); break;
        }

        VkCommandBufferBeginInfo beginInfo
        {
            VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
            nullptr, 0, nullptr
        };
        vkResetCommandBuffer(commandBuffer, 0);
        vkBeginCommandBuffer(commandBuffer, &beginInfo);
        {
            VkClearValue clearColor = { {0.2f, 0.2f, 0.4f, 1.0f} };
            VkRenderPassBeginInfo renderPassInfo
            {
                VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
                nullptr, app.getRenderPass(),
                app.getSwapchainFramebuffers()[imageIndex],
                { {0, 0}, {(uint32_t)width, (uint32_t)height} },
                1, &clearColor
            };
            vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
            // TODO
            vkCmdEndRenderPass(commandBuffer);
        }
        vkEndCommandBuffer(commandBuffer);

        app.submitAndPresent(waitNextImage, waitSubmission, waitFence, imageIndex);
        glfwPollEvents();
    }

    vkWaitForFences(app.getDevice(), 1, &waitFence, VK_TRUE, UINT64_MAX);
    return 0;
}